﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using ITAP.Core.Calculations;

using ITAP.ContourBuilder.Components;
using ITAP.Core.Modelling.Primitives;
using ITAP.ContourBuilder.Manipulation;
using System.Windows.Forms;
using ITAP.ContourBuilder.Components.Dimensions;
using ITAP.Core.Modelling;

namespace ITAP.ContourBuilder.ContourRender
{
    public class SimpleContourRender : IContourVisitor, IDisposable
    {
        //private Pen currentPen;
        //private Brush currentBrush;
        private DimensionRender dimensionRender;
        private List<ICommand> commandsToDraw;

        public List<ICommand> CommandsToDraw
        {
            get { return commandsToDraw; }
        }
        protected PrimitiveRender primitiveRender;
        protected Graphics graphics;
        protected int screenWidth;
        protected int screenHeight;
        protected RectangleF modelRectangle;
        protected ModelPoint centerPoint;
        protected ModelCoordinateConverter coordinateConverter;

        protected List<Type> renderPriority;

        public SimpleContourRender()
        {
            if (this is DimensionRender)
                return;

            dimensionRender = new DimensionRender();
            commandsToDraw = new List<ICommand>();
            primitiveRender = new PrimitiveRender();
            this.renderPriority = new List<Type>();
            this.renderPriority.Add(typeof(Dimension));
            this.renderPriority.Add(typeof(ContourElement));
            this.renderPriority.Add(typeof(ContourConnector));
            this.renderPriority.Add(typeof(ContourElementHook));
            this.renderPriority.Add(typeof(ContourModel));
            this.renderPriority.Add(typeof(ContourText));
            this.renderPriority.Add(typeof(ContourCommentLine));
            this.renderPriority.Add(typeof(ContourHole));
        }

        public virtual void Initialize(Graphics graphics, int screenWidth, int screenHeight, RectangleF modelRectangle, ModelCoordinateConverter coordinateConverter, ModelPoint centerPoint)
        {
            primitiveRender.Initialize(graphics, screenWidth, screenHeight, modelRectangle, coordinateConverter, centerPoint);
            this.graphics = graphics;
            this.screenWidth = screenWidth;
            this.screenHeight = screenHeight;
            this.modelRectangle = modelRectangle;
            this.centerPoint = centerPoint;
            this.coordinateConverter = coordinateConverter;
            this.coordinateConverter.RecalculateAutoScale(screenWidth, screenHeight, modelRectangle);          
        }

        #region IContourVisitor Members

        public void Visit(ContourModel model)
        {

        }

        public void Visit(ContourContainer container)
        {
            lock (container)
            {
                RectangleF screenRectangle = coordinateConverter.ModelRectangleToScreenRectangle(container.BoundingBox, screenWidth, screenHeight, modelRectangle, centerPoint);
                RectangleF innerScreenRectangle = coordinateConverter.ModelRectangleToScreenRectangle(container.InnerBoundingBox, screenWidth, screenHeight, modelRectangle, centerPoint);
                //graphics.FillRectangle(Brushes.LightGray, Rectangle.Truncate(screenRectangle));
                //graphics.FillRectangle(Brushes.White, Rectangle.Truncate(innerScreenRectangle));

                if (innerScreenRectangle.Width != 0 && innerScreenRectangle.Height != 0)
                {
                    graphics.DrawRectangle(Pens.LightGray, Rectangle.Truncate(screenRectangle));
                    graphics.DrawRectangle(Pens.LightGray, Rectangle.Truncate(innerScreenRectangle));
                }

                foreach (Type type in this.renderPriority)
                {
                    IEnumerable<IContourComponent> allComponents = container.GetAllInnerComponents();
                    foreach (IContourComponent component in allComponents)
                    {
                        if (type == typeof(Dimension) && component is Dimension)
                            this.Visit(component as Dimension);
                        else
                        {
                            if (component.GetType() == type)
                                component.AcceptVisitor(this);
                        }
                    }
                }

                lock (this.CommandsToDraw)
                {
                    foreach (ICommand commandToDraw in commandsToDraw)
                    {
                        if (commandToDraw.State == CommandState.Execute)
                            commandToDraw.DrawOnGraphics(graphics, screenWidth, screenHeight, modelRectangle, this.centerPoint, this.coordinateConverter);
                    }
                }
            }
        }

        public void Visit(ContourElement element)
        {
            if (element.Primitive == null)
                return;

            PointF startPoint = this.coordinateConverter.ModelPointToScreenPoint(element.StartPoint, screenWidth, screenHeight, modelRectangle, this.centerPoint);
            PointF endPoint = this.coordinateConverter.ModelPointToScreenPoint(element.EndPoint, screenWidth, screenHeight, modelRectangle, this.centerPoint);
            if (element.Height == 0)
            {
                if(element.Selected)
                    this.graphics.DrawLine(Pens.Green, startPoint, endPoint);
                else
                    this.graphics.DrawLine(Pens.Black, startPoint, endPoint);
            }
            else
            {
                RectangleF rect;
                float startAngle;
                float sweepAngle;
                this.GetArcParameters(element.Primitive as Circle, out rect, out startAngle, out sweepAngle);
                if(element.Selected)
                    this.graphics.DrawArc(Pens.Green, rect, startAngle, sweepAngle);
                else
                    this.graphics.DrawArc(Pens.Black, rect, startAngle, sweepAngle);
            }
        }

        public void Visit(ContourConnector connector)
        {
            PointF point = this.coordinateConverter.ModelPointToScreenPoint(connector.Position, screenWidth, screenHeight, modelRectangle, centerPoint);
            //RectangleF rect = this.coordinateConverter.ModelRectangleToScreenRectangle(connector.BoundingBox, screenWidth, screenHeight, modelRectangle, centerPoint);

            if (connector.Selected)
                this.graphics.FillEllipse(Brushes.Green, point.X - connector.RadiusInPixels, point.Y - connector.RadiusInPixels, connector.RadiusInPixels * 2, connector.RadiusInPixels * 2);
            else
                this.graphics.FillEllipse(Brushes.Red, point.X - connector.RadiusInPixels, point.Y - connector.RadiusInPixels, connector.RadiusInPixels * 2, connector.RadiusInPixels * 2);
            //primitiveRender.Pen = Pens.Green;
            //IPrimitive offsetPrimitive = connector.Primitive.GetOffsetPrimitive(1);
            //offsetPrimitive.AcceptVisitor(primitiveRender);

        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            //currentPen.Dispose();
            //currentBrush.Dispose();
        }

        #endregion

        public virtual void GetArcParameters(Circle circle, out RectangleF rectangle, out float startAngle, out float sweepAngle)
        {
            PointF center = coordinateConverter.ModelPointToScreenPoint(circle.Center, screenWidth, screenHeight, modelRectangle, this.centerPoint);
            float radius = coordinateConverter.ModelMeasureToScreenMeasure((float)circle.Radius, screenWidth, screenHeight, modelRectangle);
            rectangle = new RectangleF(center.X - radius, center.Y - radius, radius * 2, radius * 2);
            if (circle.Height > 0)
            {
                startAngle = (float)(360 - circle.EndAngle.Value * 180 / Math.PI);
            }
            else
            {
                startAngle = (float)(360 - circle.StartAngle.Value * 180 / Math.PI);
            }
            sweepAngle = (float)(circle.Angle.Value * 180 / Math.PI);
        }


        #region IContourVisitor Members


        public void Visit(ContourElementHook hook)
        {
            RectangleF rect = coordinateConverter.ModelRectangleToScreenRectangle(hook.BoundingBox, screenWidth, screenHeight, modelRectangle, centerPoint);
            if (!hook.Selected)
            {
                switch (hook.HookType)
                {
                    case ContourElementPoints.Start:
                        graphics.FillEllipse(Brushes.LightYellow, rect);
                        break;
                    case ContourElementPoints.Center:
                        graphics.FillEllipse(Brushes.LightGreen, rect);
                        break;
                    case ContourElementPoints.End:
                        graphics.FillEllipse(Brushes.LightBlue, rect);
                        break;
                }
            }
            else
            {
                switch (hook.HookType)
                {
                    case ContourElementPoints.Start:
                        graphics.FillEllipse(Brushes.Yellow, rect);
                        break;
                    case ContourElementPoints.Center:
                        graphics.FillEllipse(Brushes.Green, rect);
                        break;
                    case ContourElementPoints.End:
                        graphics.FillEllipse(Brushes.Blue, rect);
                        break;
                }
            }
            graphics.DrawEllipse(Pens.DarkGray, rect);
        }

        #endregion

        #region IContourVisitor Members


        public void Visit(ContourText text)
        {
            text.StringSizeFromRender = graphics.MeasureString(text.Text, text.Font);
            SolidBrush brush;
            if (!text.Selected)
                brush = new SolidBrush(text.FontColor);
            else
                brush = new SolidBrush(Color.Green);

            PointF position = this.coordinateConverter.ModelPointToScreenPoint(text.Position, screenWidth, screenHeight, modelRectangle, centerPoint);
            if (text.TextType == TextRenderType.UseScreenTextSize)
                graphics.DrawString(text.Text, text.Font, brush, position);
            else
            {
                Font modelFont = new Font(text.Font.FontFamily, text.Font.Size * (float)GlobalData.FontSizeToScreenSizeFactor * (float)coordinateConverter.Scale, text.Font.Style);
                graphics.DrawString(text.Text, modelFont, brush, position);
            }
            
            if(text.CommentLines.Count<IEndedContourComponent>() != 0)
            {
                RectangleF rect = coordinateConverter.ModelRectangleToScreenRectangle(text.BoundingBox, screenWidth, screenHeight, modelRectangle, centerPoint);
                PointF startPoint = new PointF(rect.X, rect.Y + rect.Height);
                PointF endPoint = new PointF(rect.X + rect.Width, rect.Y + rect.Height);
                graphics.DrawLine(Pens.Black, startPoint, endPoint);
            }
            brush.Dispose();

        }

        #endregion

        #region IContourVisitor Members


        public virtual void Visit(ContourCommentLine commentLine)
        {
            PointF startPoint = coordinateConverter.ModelPointToScreenPoint(commentLine.StartPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
            PointF endPoint = coordinateConverter.ModelPointToScreenPoint(commentLine.EndPoint, screenWidth, screenHeight, modelRectangle, centerPoint);
            if (!commentLine.Selected)
                graphics.DrawLine(Pens.Black, startPoint, endPoint);
            else
                graphics.DrawLine(Pens.Green, startPoint, endPoint);
        }
        #endregion

        #region IContourVisitor Members


        public void Visit(Dimension dimension)
        {
            dimensionRender.Initialize(graphics, screenWidth, screenHeight, modelRectangle, coordinateConverter, centerPoint);
            dimension.AcceptDimensionVisitor(dimensionRender);
        }

        #endregion



        #region IContourVisitor Members


        public void Visit(ContourHole hole)
        {
            RectangleF bbox = hole.BoundingBox;
            RectangleF screenBbox = coordinateConverter.ModelRectangleToScreenRectangle(bbox, screenWidth, screenHeight, modelRectangle, centerPoint);
            if(!hole.Selected)
                graphics.DrawEllipse(Pens.Black, screenBbox);            
            else
                graphics.DrawEllipse(Pens.Green, screenBbox);            
        }

        #endregion
    }
}
